package LabReport;

/**
 * @Author Zhang Chao
 * @Date 2021/6/13 11:18
 * @Version 1.0
 */

import org.junit.jupiter.api.Test;

import java.io.*;
import java.util.Scanner;

/**
 * 1.java使用File文件工具进行文件指定类型文件筛选
 * 2.使用id流对文件进行copy
 * 3.使用序列化与反序列化对对象进行存储以及复原
 */
public class LabReport1 {


}

/**
 * 1.java使用File文件工具进行文件指定类型文件筛选
 */
class TestDriver1 {
    @Test
    public void test() {
        // 1、输入目录名和关键字
        Scanner scanner = new Scanner(System.in);
        System.out.println("请入查找的文件目录（绝对位置）：");
        // 文件目录
        String strDirectoryName = scanner.nextLine();
        System.out.println("请入出查找的关键字：");
        // 查找的关键字
        String strKeyWords = scanner.nextLine();
        // 创建查找的目录文件对象
        File fileDirectory = new File(strDirectoryName);

        // 调用文件的处理方法进行查找处理
        fetchByKeyWords(fileDirectory, strKeyWords);

    }

    /**
     * 通过递归查找含有关键字的文件
     *
     * @param fileDirectory 被查找的目录
     * @param strKeyWords   查找的关键字
     */
    public static void fetchByKeyWords(File fileDirectory, String strKeyWords) {
        /*
          递归算法：
             1、要有递归结束条件；
             2、要自己调用自己；

             补充：递归的算法效率很低，时间效率和空间效率都很低；
                  好处：代码逻辑简单；
         */

        // 2、判断目录是否存在，如果存在,在获取该目录下的所有的文件对象

        // 判断文件是否存在
        if (fileDirectory.exists()) {
            // 判断file对象是目录还是文档文件
            if (fileDirectory.isDirectory()) {
                // 目录
                if (fileDirectory.getName().toLowerCase().contains(strKeyWords.toLowerCase())) {
                    // 输出该文件信息
                    System.out.println(fileDirectory.getAbsolutePath() + "   ----   目录");
                }
                // 获取该目录下的所有的文件对象
                File[] subFiles = fileDirectory.listFiles(new FileFilter() {
                    @Override
                    public boolean accept(File eachFile) {
                        // 如果是目录，则返回true，放到结果数组中；
                        // 如果是文件，则需要判断文件名是否包含关键字，不包含关键字的，则不需要放入结果数组中
                        if (eachFile.isDirectory()) {
                            // 是目录
                            return true;
                        } else {
                            // 是文件，则需要进行筛选
                            if (eachFile.getName().toLowerCase().contains(strKeyWords.toLowerCase())) {
                                // 返回true
                                return true;
                            } else {
                                return false;
                            }
                        }
                    }
                });

                // 3、依次遍历获取的文件对象，并查找含有关键字的文件对象，进行输出显示
                // 依次遍历数组中每个文件对象，判断是否含有关键字
                for (File subF : subFiles) {
                    // 判断是否含有关键字
                    // 因为当前文件是一个目录，则需要进行递归再次处理
                    fetchByKeyWords(subF, strKeyWords);
                }
            } else {
                // 文档文件，直接输出即可
                // 输出该文件信息
                System.out.println(fileDirectory.getAbsolutePath() + "   ----   文件");
            }

        } else {
            System.out.println("没有该文件");
        }
    }
}

/**
 * 2.使用id流对文件进行copy
 */
class TestDriver02 {

    /**
     * IO流的案例： 读取文件并写入新文件
     * 输入 输出 是相对于内存的
     *
     * 问题：如何写相对路径
     */
    @Test
    public void test01(){
        try {
            //1.声明一个输入流对象
            InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("F:\\Project-space\\java_demo\\java_basics_demo\\src\\LabReport\\file\\file1.txt"),"UTF-8");
            //2.声明一个对象输出流
            OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream("F:\\Project-space\\java_demo\\java_basics_demo\\src\\LabReport\\file\\file2.txt"), "UTF-8");


            //3.读取文件内容
            // 声明一个用于读取文件的临时缓存的数组对象，长度可以自定义
            char[] chars = new char[1024];
            // 读取的长度
            int len = -1;
            // 读取数组
            System.out.println("读取的内容：");
            do {
                len = inputStreamReader.read(chars, 0, 1024);
                if (len > 0) {
                    String string = new String(chars, 0, len);
                    System.out.print(string);

                    // 业务逻辑处理，把读取的内容重新写入新的文件中
                    outputStreamWriter.write(chars, 0, len);
                    // 清空和刷新写出缓存区
                    outputStreamWriter.flush();
                }
            }while(len>0);

            //4.回收资源s
            // 业务处理完成之后，回收资源 --- 资源回收也可以写在 finally 部分
            inputStreamReader.close();
            outputStreamWriter.close();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

/**
 * 3.使用序列化与反序列化对对象进行存储以及复原
 */
class TestDriver03 {

    @Test
    public void test() {
        /*
        对象流：要求被对象流写出的类必须实现 Serializable 接口，表示这个对象可以被对象流通过序列化的方式进行 IO/网络传输操作；
             序列化：在IO、网络、不同存储介质之间进行对象信息传输的时候，所采用的一种方式；序列化主要是在跨介质的传输中保证数据对象的有序性；
             传输之前需要进行序列化处理，按照有序的顺序进行分块传输；在接收方，通过有序的编号进行复原的过程叫做反序列化；
             transient关键字：将被修饰的属性排除序列化的机制；被其修饰的属性，在序列化时该属性的值不会被进行保存，在反序列化时该属性值会赋默认值；

         // 扩展：
         深克隆和浅克隆；[深复制和浅复制]
            浅克隆： 给一个对象起另外一个名字；两个变量名同时执行同一个对象内存，叫做浅克隆；
            深克隆： 对一个已有的对象，在内存中新开辟一块空间，将已有对象的内容对应复制过来，这样在内存中有两个一摸一样的对象叫做深克隆。（克隆羊多莉）
                    一般使用对象流来实现对象的深克隆复制；
                    因为： 基本类型在克隆的时候都是深克隆，但是引用类型，在克隆的时候一般默认都是浅克隆，所以深克隆需要手动来实现。
         */

        // 对象输出流
        writeObject();

        //////////////////////////////////////////
        // 从对象文件中读取一个对象到内存中
        Object object = readObject();
        System.out.println("反序列化之后的内容：" + object);

        // 再次读取一个对象到内存中
        Object object1 = readObject();

        // 请确认： object 和 object1 是不是同一个对象？
        System.out.println("object == object1:" + (object == object1));  //不是同一个了
    }

    /**
     * 使用对象流读取一个对象文件到内存中
     *
     * @return 读取到内存的对象
     */
    private static Object readObject() {
        // 返回的读取的对象
        Object object = null;
        // 使用对象输入流，将文件中的colaEmployee对象的信息以字节流的形式读取到内存中，其会复原为一个内存中的对象
        try {
            ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("F:\\Project-space\\java_demo\\java_basics_demo\\src\\LabReport\\emp_data.dat"));
            // 把文件中的对象写到内存中中
            object = objectInputStream.readObject();
            // 提示信息
            System.out.println("ColaEmployee已经读取到内存中");
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        //返回值
        return object;
    }

    /**
     * 将内存中的对象使用对象流写出到对象文件中
     */
    private static void writeObject() {
        //创建一个对象
        objectIO.ColaEmployee colaEmployee = new objectIO.ColaEmployee("安东尼", 4, 5);
        // 使用对象输出流，将内存中的colaEmployee对象的信息以字节流的形式写出到指定文件中
        try {
            // 声明创建一个对象输入流
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("F:\\Project-space\\java_demo\\java_basics_demo\\src\\LabReport\\emp_data.dat"));
            // 把内存中的对象写出到文件中
            objectOutputStream.writeObject(colaEmployee);
            // 提示信息
            System.out.println("ColaEmployee已经写出到对象文件中");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

class ColaEmployee implements Serializable {
    // 属性
    private String empName;
    //transient 关键字修饰的  表示不参与序列化！
    private transient int empBirthMonth;
    private int month;

    //行为
    public ColaEmployee(String empName, int empBirthMonth, int month) {
        this.empName = empName;
        this.empBirthMonth = empBirthMonth;
        this.month = month;
    }

    public String getEmpName() {
        return empName;
    }

    public void setEmpName(String empName) {
        this.empName = empName;
    }

    public int getEmpBirthMonth() {
        return empBirthMonth;
    }

    public void setEmpBirthMonth(int empBirthMonth) {
        this.empBirthMonth = empBirthMonth;
    }

    public int getMonth() {
        return month;
    }

    public void setMonth(int month) {
        this.month = month;
    }

    /**
     * 根据员工是否当月生日，获取生日金工资
     *
     * @return 生日金工资
     */
    public double getSalary() {
        // 返回值
        double salary = 0.0;
        // 判断是否的当月过生日，如果是，则工资为100元生日金
        if (month == empBirthMonth) {
            salary = 100;
        }
        // 返回值
        return salary;
    }
    @Override
    public String toString() {
        return "ColaEmployee{" +
                "empName='" + empName + '\'' +
                ", empBirthMonth=" + empBirthMonth +
                ", month=" + month +
                '}';
    }
}

